#ifndef Net_Socket_INCLUDED
#define Net_Socket_INCLUDED

#include "Poco/Net/SocketDefs.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Timespan.h"

namespace Poco {
namespace Net {

class Net_API Socket
	/// This class encapsulates the Berkeley sockets API.
	/// 
{
public:
	typedef std::vector<Socket>		SocketList;
	enum SelectMode
	{
		SELECT_READ  = 1,
		SELECT_WRITE = 2,
		SELECT_ERROR = 4
	};
	enum SocketConst
	{
		LISTEN_BACKLOG=1024,
		MAXOPENFD=100*1024
	};
	/*
	// valid __socket_type
	SOCK_STREAM = 1,		// Sequenced, reliable, connection-based byte streams.  
	SOCK_DGRAM = 2,			//Connectionless, unreliable datagrams of fixed maximum length.  
	SOCK_RAW = 3,			//Raw protocol interface.  
	SOCK_RDM = 4,			//Reliably-delivered messages. 
	SOCK_SEQPACKET = 5,		//Sequenced, reliable, connection-based, datagrams of fixed maximum length. 
	SOCK_DCCP = 6,			//Datagram Congestion Control Protocol. 
	SOCK_PACKET = 10,		//Linux specific way of getting packets at the dev level.  For writing rarp and
							//other similar things on the user level. 
	*/
	Socket(__socket_type sockettype = SOCK_STREAM);
		/// Creates a Socket
	
	Socket(const Socket& socket);
		/// Copy constructor.
		///
		/// Attaches the Socket from the other socket and
		/// increments the reference count of the Socket.
	
	Socket(poco_socket_t sockfd,__socket_type sockettype = SOCK_STREAM);
		/// Creates a Socket using the given native socket.
		
	Socket& operator = (const Socket& socket);
		/// Assignment operator.
		///
		/// Releases the socket's SocketImpl and
		/// attaches the SocketImpl from the other socket and
		/// increments the reference count of the SocketImpl.
		
	virtual ~Socket();
		/// close and releases
		
	bool operator == (const Socket& socket) const;
		/// Compares the fd.

	bool operator != (const Socket& socket) const;
		/// Compares the fd.

	bool operator <  (const Socket& socket) const;
		/// Compares the fd.
	
	bool operator <= (const Socket& socket) const;
		/// Compares the fd.

	bool operator >  (const Socket& socket) const;
		/// Compares the fd.

	bool operator >= (const Socket& socket) const;
		/// Compares the fd.
	
	void close();
		/// Closes the socket. need to be called by the user.
	
	static int select(SocketList& readList, SocketList& writeList, SocketList& exceptList, const Poco::Timespan& timeout);
		/// Determines the status of one or more sockets, 
		/// using a call to select().
		///
		/// ReadList contains the list of sockets which should be
		/// checked for readability.
		///
		/// WriteList contains the list of sockets which should be
		/// checked for writeability.
		///
		/// ExceptList contains a list of sockets which should be
		/// checked for a pending error.
		///
		/// Returns the number of sockets ready.
		///
		/// After return, 
		///   * readList contains those sockets ready for reading,
		///   * writeList contains those sockets ready for writing,
		///   * exceptList contains those sockets with a pending error.
		///
		/// If the total number of sockets passed in readList, writeList and
		/// exceptList is zero, select() will return immediately and the
		/// return value will be 0.
		///
		/// If one of the sockets passed to select() is closed while
		/// select() runs, select will return immediately. However,
		/// the closed socket will not be included in any list.
		/// In this case, the return value may be greater than the sum
		/// of all sockets in all list.
	
	bool poll(const Poco::Timespan& timeout, int mode) const;
		/// Determines the status of the socket, using a 
		/// call to poll() or select().
		/// 
		/// The mode argument is constructed by combining the values
		/// of the SelectMode enumeration.
		///
		/// Returns true if the next operation corresponding to
		/// mode will not block, false otherwise.
	
	
	int acceptConnection(SocketAddress& clientAddr);
		/// Get the next completed connection from the
		/// socket's completed connection queue.
		///
		/// If the queue is empty, waits until a connection
		/// request completes.
		///
		/// Returns a new TCP socketfd for the connection
		/// with the client.
		///
		/// The client socket's address is returned in clientAddr.
	
	int connect(const SocketAddress& address);
		/// return 0:success, other == errno
		/// Initializes the socket and establishes a connection to 
		/// the TCP server at the given address.
		///
		/// Can also be used for UDP sockets. In this case, no
		/// connection is established. Instead, incoming and outgoing
		/// packets are restricted to the specified address.

	int connect(const SocketAddress& address, const Poco::Timespan& timeout);
		/// return 0:success, other == errno
		/// Initializes the socket, sets the socket timeout and 
		/// establishes a connection to the TCP server at the given address.

	int connectNB(const SocketAddress& address);
		/// return 0:success, other == errno
		/// Initializes the socket and establishes a connection to 
		/// the TCP server at the given address. Prior to opening the
		/// connection the socket is set to nonblocking mode.
	
	void bind(const SocketAddress& address, bool reuseAddress = false);
		/// Bind a local address to the socket.
		///
		/// This is usually only done when establishing a server
		/// socket. TCP clients should not bind a socket to a
		/// specific address.
		///
		/// If reuseAddress is true, sets the SO_REUSEADDR and SO_REUSEPORT
		/// socket option.

	void bind6(const SocketAddress& address, bool reuseAddress = false, bool ipV6Only = false);
		/// Bind a local IPv6 address to the socket.
		///
		/// This is usually only done when establishing a server
		/// socket. TCP clients should not bind a socket to a
		/// specific address.
		///
		/// If reuseAddress is true, sets the SO_REUSEADDR
		/// socket option.
		///
		/// The given address must be an IPv6 address. The
		/// IPPROTO_IPV6/IPV6_V6ONLY option is set on the socket
		/// according to the ipV6Only parameter.
		///
		/// If the library has not been built with IPv6 support,
		/// a Poco::NotImplementedException will be thrown.

	void listen(int backlog = LISTEN_BACKLOG);
		/// Puts the socket into listening state.
		///
		/// The socket becomes a passive socket that
		/// can accept incoming connection requests.
		///
		/// The backlog argument specifies the maximum
		/// number of connections that can be queued
		/// for this socket.

	void shutdownRecv();
		/// Shuts down the receiving part of the socket connection.
		
	void shutdownSend();
		/// Shuts down the sending part of the socket connection.
		
	void shutdown();
		/// Shuts down both the receiving and the sending part
		/// of the socket connection.
	
	int sendBytes(const char* buffer, int length, int flags = 0);
		/// Sends the contents of the given buffer through
		/// the socket.
		///
		/// Returns the number of bytes sent, which may be
		/// less than the number of bytes specified.
		///
		/// Certain socket implementations may also return a negative
		/// value denoting a certain condition.
	
	int recvBytes(char* buffer, int length, int flags = 0);
		/// Recvs data from the socket and stores it
		/// in buffer. Up to length bytes are received.
		///
		/// Returns the number of bytes received.
		///
		/// Certain socket implementations may also return a negative
		/// value denoting a certain condition.
	
	int sendTo(const char* buffer, int length, const SocketAddress& address, int flags = 0);
		/// Sends the contents of the given buffer through
		/// the socket to the given address.
		///
		/// Returns the number of bytes sent, which may be
		/// less than the number of bytes specified.
	
	int recvFrom(char* buffer, int length, SocketAddress& address, int flags = 0);
		/// Recvs data from the socket and stores it
		/// in buffer. Up to length bytes are received.
		/// Stores the address of the sender in address.
		///
		/// Returns the number of bytes received.
	
	void sendUrgent(unsigned char data);
		/// Sends one byte of urgent data through
		/// the socket.
		///
		/// The data is sent with the MSG_OOB flag.
		///
		/// The preferred way for a socket to receive urgent data
		/// is by enabling the SO_OOBINLINE option.
	
	int available();
		/// Returns the number of bytes available that can be read
		/// without causing the socket to block.
		
	bool poll(const Poco::Timespan& timeout, int mode);
		/// Determines the status of the socket, using a 
		/// call to select().
		/// 
		/// The mode argument is constructed by combining the values
		/// of the SelectMode enumeration.
		///
		/// Returns true if the next operation corresponding to
		/// mode will not block, false otherwise.
		
	void setSendBufferSize(int size);
		/// Sets the size of the send buffer.
		
	int getSendBufferSize() const;
		/// Returns the size of the send buffer.
		///
		/// The returned value may be different than the
		/// value previously set with setSendBufferSize(),
		/// as the system is free to adjust the value.

	void setRecvBufferSize(int size);
		/// Sets the size of the receive buffer.
		
	int getRecvBufferSize() const;
		/// Returns the size of the receive buffer.
		///
		/// The returned value may be different than the
		/// value previously set with setRecvBufferSize(),
		/// as the system is free to adjust the value.

	void setSendTimeout(const Poco::Timespan& timeout);
		/// Sets the send timeout for the socket.
	
	Poco::Timespan getSendTimeout() const;
		/// Returns the send timeout for the socket.
		///
		/// The returned timeout may be different than the
		/// timeout previously set with setSendTimeout(),
		/// as the system is free to adjust the value.

	void setRecvTimeout(const Poco::Timespan& timeout);
		/// Sets the recv timeout for the socket.
		///
		/// On systems that do not support SO_RCVTIMEO, a
		/// workaround using poll() is provided.
	
	Poco::Timespan getRecvTimeout() const;
		/// Returns the receive timeout for the socket.
		///
		/// The returned timeout may be different than the
		/// timeout previously set with setRecvTimeout(),
		/// as the system is free to adjust the value.
	
	SocketAddress address() const;
		/// Returns the IP address and port number of the socket.
		
	SocketAddress peerAddress() const;
		/// Returns the IP address and port number of the peer socket.
	
	void setOption(int level, int option, int value);
		/// Sets the socket option specified by level and option
		/// to the given integer value.

	static void setOption(int sockfd,int level, int option, int value);
		/// Sets the socket option specified by level and option
		/// to the given integer value.
	
	void setOption(int level, int option, unsigned value);
		/// Sets the socket option specified by level and option
		/// to the given integer value.

	void setOption(int level, int option, unsigned char value);
		/// Sets the socket option specified by level and option
		/// to the given integer value.
		
	void setOption(int level, int option, const Poco::Timespan& value);
		/// Sets the socket option specified by level and option
		/// to the given time value.
		
	void setOption(int level, int option, const IPAddress& value);
		/// Sets the socket option specified by level and option
		/// to the given time value.
		
	void setRawOption(int level, int option, const void* value, poco_socklen_t length);
		/// Sets the socket option specified by level and option
		/// to the given time value.
	
	static void setRawOption(int sockfd,int level, int option, const void* value, poco_socklen_t length);
		/// Sets the socket option specified by level and option
		/// to the given time value.
		
	void getOption(int level, int option, int& value) const;
		/// Returns the value of the socket option 
		/// specified by level and option.
	
	static void getOption(int sockfd,int level, int option, int& value);
		/// Returns the value of the socket option 
		/// specified by level and option.
	
	void getOption(int level, int option, unsigned& value) const;
		/// Returns the value of the socket option 
		/// specified by level and option.

	void getOption(int level, int option, unsigned char& value) const;
		/// Returns the value of the socket option 
		/// specified by level and option.

	void getOption(int level, int option, Poco::Timespan& value) const;
		/// Returns the value of the socket option 
		/// specified by level and option.
	
	void getOption(int level, int option, IPAddress& value) const;
		/// Returns the value of the socket option 
		/// specified by level and option.

	void getRawOption(int level, int option, void* value, poco_socklen_t& length) const;
		/// Returns the value of the socket option 
		/// specified by level and option.	
	
	static void getRawOption(int sockfd,int level, int option, void* value, poco_socklen_t& length);
		/// Returns the value of the socket option 
		/// specified by level and option.	
	
	void setLinger(bool on, int seconds);
		/// Sets the value of the SO_LINGER socket option.
		
	void getLinger(bool& on, int& seconds) const;
		/// Returns the value of the SO_LINGER socket option.
	
	void setNoDelay(bool flag);
		/// Sets the value of the TCP_NODELAY socket option.
		
	bool getNoDelay() const;
		/// Returns the value of the TCP_NODELAY socket option.
	
	static void setNoDelay(int sockfd,bool flag);
		/// Sets the value of the TCP_NODELAY socket option for sockfd
		
	static bool getNoDelay(int sockfd);
		/// Returns the value of the TCP_NODELAY socket option for sockfd
	
	void setKeepAlive(bool flag);
		/// Sets the value of the SO_KEEPALIVE socket option.
		
	bool getKeepAlive() const;
		/// Returns the value of the SO_KEEPALIVE socket option.
	
	void setReuseAddress(bool flag);
		/// set before bind
		/// Sets the value of the SO_REUSEADDR socket option.
	
	bool getReuseAddress() const;
		/// Returns the value of the SO_REUSEADDR socket option.

	void setReusePort(bool flag);
		/// set before bind
		/// Sets the value of the SO_REUSEPORT socket option.
		/// Does nothing if the socket implementation does not
		/// support SO_REUSEPORT.
	
	bool getReusePort() const;
		/// Returns the value of the SO_REUSEPORT socket option.
		///
		/// Returns false if the socket implementation does not
		/// support SO_REUSEPORT.
		
	void setOOBInline(bool flag);
		/// Sets the value of the SO_OOBINLINE socket option.
	
	bool getOOBInline() const;
		/// Returns the value of the SO_OOBINLINE socket option.
	
	void setBroadcast(bool flag);
		/// Sets the value of the SO_BROADCAST socket option.
		
	bool getBroadcast() const;
		/// Returns the value of the SO_BROADCAST socket option.

	static void setBlocking(int sockfd,bool flag);
		/// Sets the socket in blocking mode if flag is true,
		/// disables blocking mode if flag is false.
		/// before connect or listen
	
	static bool getBlocking(int sockfd);
		/// check wether sockfd is O_NONBLOCK
	
	
	bool secure() const;
		/// Returns true iff the socket's connection is secure
		/// (using SSL or TLS).

	static bool supportsIPv4();
		/// Returns true if the system supports IPv4.
		
	static bool supportsIPv6();
		/// Returns true if the system supports IPv6.
	
	int socketError() const;
		/// Returns the value of the SO_ERROR socket option.

	poco_socket_t sockfd() const;
		/// Returns the socket descriptor for the
		/// underlying native socket.

	void ioctl(poco_ioctl_request_t request, int& arg);
		/// A wrapper for the ioctl system call.

	void ioctl(poco_ioctl_request_t request, void* arg);
		/// A wrapper for the ioctl system call.

#if defined(POCO_OS_FAMILY_UNIX)
	static int fcntl(int sockfd,poco_fcntl_request_t request);
		/// A wrapper for the fcntl system call.

	static int fcntl(int sockfd,poco_fcntl_request_t request, long arg);
		/// A wrapper for the fcntl system call.
#endif

	bool initialized() const;
		/// Returns true iff the underlying socket is initialized.
	
	void init(int af);
		/// Creates the underlying system socket for the given
		/// address family.
		///
		/// Normally, this method should not be called directly, as
		/// socket creation will be handled automatically. There are
		/// a few situations where calling this method after creation
		/// of the Socket object makes sense. One example is setting
		/// a socket option before calling bind() on a ServerSocket.
	
	void initSocket(int af, int type, int proto = 0);
		/// Creates the underlying native socket.
		///
		/// The first argument, af, specifies the address family
		/// used by the socket, which should be either AF_INET or
		/// AF_INET6.
		///
		/// The second argument, type, specifies the type of the
		/// socket, which can be one of SOCK_STREAM, SOCK_DGRAM
		/// or SOCK_RAW.
		///
		/// The third argument, proto, is normally set to 0,
		/// except for raw sockets.

	void reset(poco_socket_t fd = POCO_INVALID_SOCKET);
		/// Allows subclasses to set the socket manually, iff no valid socket is set yet.

	static int lastError();
		/// Returns the last error code.

	static void error();
		/// Throws an appropriate exception for the last error.

	static void error(const std::string& arg);
		/// Throws an appropriate exception for the last error.

	static void error(int code);
		/// Throws an appropriate exception for the given error code.
		
	static void error(int code, const std::string& arg);
		/// Throws an appropriate exception for the given error code.

	__socket_type getSocketType() const;
	
	void setSocketType(__socket_type sockettype);
	
private:
	
	poco_socket_t 		m_sockfd;
	__socket_type 		m_socketType;
	bool          		m_blocking;
};


/*
 * inlines
 */


inline bool Socket::operator == (const Socket& socket) const
{
	return m_sockfd == POCO_INVALID_SOCKET?false:m_sockfd == socket.m_sockfd;
}


inline bool Socket::operator != (const Socket& socket) const
{
	return m_sockfd == POCO_INVALID_SOCKET?false:m_sockfd != socket.m_sockfd;
}


inline bool Socket::operator <  (const Socket& socket) const
{
	return m_sockfd == POCO_INVALID_SOCKET?false:m_sockfd < socket.m_sockfd;
}


inline bool Socket::operator <= (const Socket& socket) const
{
	return m_sockfd == POCO_INVALID_SOCKET?false:m_sockfd <= socket.m_sockfd;
}


inline bool Socket::operator >  (const Socket& socket) const
{
	return m_sockfd == POCO_INVALID_SOCKET?false:m_sockfd > socket.m_sockfd;
}


inline bool Socket::operator >= (const Socket& socket) const
{
	return m_sockfd == POCO_INVALID_SOCKET?false:m_sockfd >= socket.m_sockfd;
}


inline poco_socket_t Socket::sockfd() const
{
	return m_sockfd;
}


inline bool Socket::initialized() const
{
	return m_sockfd != POCO_INVALID_SOCKET;
}


inline int Socket::lastError()
{
	return errno;
}


inline bool Socket::secure() const
{
	return false;
}


inline bool Socket::supportsIPv4()
{
	return true;
}


inline bool Socket::supportsIPv6()
{
#if defined(POCO_HAVE_IPv6)
	return true;
#else
	return false;
#endif
}

} } // namespace Poco::Net

#endif















